import torch  # 导入PyTorch库，用于深度学习模型的构建和训练
import numpy as np  # 导入NumPy库，用于高效的多维数组和矩阵运算
from torch.utils.data import Dataset  # 从PyTorch的utils.data模块导入Dataset类，用于自定义数据集
from torch.utils.data import DataLoader  # 从PyTorch的utils.data模块导入DataLoader类，用于数据加载和批处理

# 准备数据集

class DiabetesDataset(Dataset):  # 定义一个继承自Dataset的类，用于加载糖尿病数据集
    def __init__(self, filepath):  # 初始化方法，接收文件路径作为参数
        xy = np.loadtxt(filepath, delimiter=',', dtype=np.float32)  # 使用NumPy加载CSV文件，指定分隔符为逗号，数据类型为float32
        self.len = xy.shape[0]  # 设置数据集长度为数据的行数
        self.x_data = torch.from_numpy(xy[:, :-1])  # 将数据集中的特征部分转换为PyTorch张量，不包括最后一列（目标值）
        self.y_data = torch.from_numpy(xy[:, [-1]])  # 将数据集中的目标值部分转换为PyTorch张量，只取最后一列

    def __getitem__(self, index):  # 重写__getitem__方法，用于按索引获取单个样本
        return self.x_data[index], self.y_data[index]  # 返回特征和对应的目标值

    def __len__(self):  # 重写__len__方法，返回数据集的长度
        return self.len

# 创建数据集实例，并设置DataLoader
dataset = DiabetesDataset('diabetes.csv')  # 创建DiabetesDataset实例，加载糖尿病数据集
train_loader = DataLoader(dataset=dataset, batch_size=32, shuffle=True, num_workers=0)  # 创建DataLoader实例，设置批大小为32，打乱数据，不使用多线程

# 设计模型

class Model(torch.nn.Module):  # 定义一个继承自torch.nn.Module的类，用于构建神经网络模型
    def __init__(self):  # 初始化方法
        super(Model, self).__init__()  # 调用父类的初始化方法
        self.linear1 = torch.nn.Linear(8, 6)  # 定义第一个全连接层，输入特征数为8，输出特征数为6
        self.linear2 = torch.nn.Linear(6, 4)  # 定义第二个全连接层，输入特征数为6，输出特征数为4
        self.linear3 = torch.nn.Linear(4, 1)  # 定义第三个全连接层，输入特征数为4，输出特征数为1（预测值）
        self.sigmoid = torch.nn.Sigmoid()  # 定义Sigmoid激活函数

    def forward(self, x):  # 定义前向传播方法
        x = self.sigmoid(self.linear1(x))  # 输入数据经过第一个全连接层和Sigmoid激活函数
        x = self.sigmoid(self.linear2(x))  # 经过第二个全连接层和Sigmoid激活函数
        x = self.sigmoid(self.linear3(x))  # 经过第三个全连接层和Sigmoid激活函数，得到最终的预测值
        return x

# 创建模型实例
model = Model()

# 构造损失函数和优化器
criterion = torch.nn.BCELoss(reduction='mean')  # 使用二元交叉熵损失函数，计算平均损失
optimizer = torch.optim.SGD(model.parameters(), lr=0.01)  # 使用随机梯度下降优化器，学习率设置为0.01

# 训练循环：前向传播、反向传播、参数更新
if __name__ == '__main__':
    for epoch in range(100):  # 训练100个周期
        for i, data in enumerate(train_loader, 0):  # 遍历DataLoader中的每个批次数据，enumerate同时返回索引和数据
            inputs, labels = data  # 解包数据，得到特征和标签
            y_pred = model(inputs)  # 执行前向传播，得到预测值
            loss = criterion(y_pred, labels)  # 计算损失
            print(epoch, i, loss.item())  # 打印当前周期、批次索引和损失值

            optimizer.zero_grad()  # 清零梯度
            loss.backward()  # 执行反向传播，计算梯度

            optimizer.step()  # 更新模型参数